Skip to content

release: 1.11.0 — dark mode text color, body/html color, :root fix (#20), block plugins on custom tags (#22), docs fixes - #21

Merged
vietnguyentuan2019 merged 7 commits into
mainfrom
fix/dark-mode-base-style
Oct 3, 2026
Merged

vietnguyentuan2019 merged 7 commits into
mainfrom
fix/dark-mode-base-style

Conversation

@vietnguyentuan2019

@vietnguyentuan2019 vietnguyentuan2019 commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Release candidate for 1.11.0. Fixes #20 and includes #22 (merge this one; GitHub will mark #22 as merged).

What ships

Package Version Why
hyper_render_core 1.10.0 → 1.11.0 dark-theme default, body/html color, :root fix, contrast guard, block plugins on custom tags (#22), deprecates dead HyperRenderTheme
hyper_render 1.10.0 → 1.11.0 HyperViewer(textColor:), theme-aware default; requires core ^1.11.0
hyper_render_epub 0.1.2 → 0.1.3 EpubReader(textColor:); requires root/core ^1.11.0
hyper_render_clipboard 1.7.2 → 1.7.3 docs only (README/guide/dartdoc showed HyperViewer(imageClipboardHandler:), which never existed)
html, markdown, highlight, math, devtools unchanged lib/ identical to the published versions; their core ^1.x constraints still resolve

Engine / API

  • Does this support dark mode? #20 Unstyled text follows a dark Theme (colorScheme.onSurface); light themes unchanged. HyperViewer(textColor:) overrides it. body { color } / html { color } now apply (they never matched); :root matches only the document root (1.10.0's parent == null check also matched top-level blocks). Elements with their own light background keep readable text (3:1 guard, only active when a theme/textColor supplies a default).
  • fix(core): block plugins on custom tags were laid out at 0x0 and never painted #22 Block plugins on custom tags (<info-box>) were laid out at 0×0 and never painted.
  • HyperRenderTheme / HyperRenderThemeData deprecated (nothing read them).

Behavior changes (also in CHANGELOG + MIGRATION_GUIDE)

  1. A surface that stays light under a dark Theme (white email pane) now gets light text — pass textColor:.
  2. Content declaring body { color } now renders in it.
  3. :root { … } no longer styles every top-level block.

Docs corrected (each checked against the code)

Clipboard README/guide/example/dartdoc, root README (clipboard + epub snippets used APIs that don't exist), example/MULTIMEDIA_EXAMPLES.md (HyperViewer(mediaBuilder:), a nonexistent HyperMediaBuilder), customCss priority wording. .pubignore now excludes *.log / .DS_Store (the 32 MB root archive was packing local flutter_0N.log crash reports).

Verification

  • Tests: new dark_mode_text_color_test (39, pixel-based), dark_mode_hardening_test (12: security / stress / performance), block_plugin_custom_tag_test (7), epub +5, clipboard documented_usage_test (+2). Mutation-checked: reverting :root, the host override, html/body ordering, the guard, or the plugin fix each fails tests. 100% of the lines added in hyper_viewer.dart, resolver.dart and the layout fix are exercised.
  • Counts: root+core 2502; html 73, markdown 29, highlight 17, math 6, clipboard 10, epub 53, devtools 19, devtools_ui 6; example 44. flutter analyze + dart format clean everywhere.
  • Demo app, all 27 demos, 0 FlutterError, logs free of Layout Warning / overflow: macOS light, macOS dark, iOS simulator light (after the fixes), iOS dark. Found and fixed by this: AI streaming demo 21px overflow, ListTile-inside-ColoredBox assertions and an overflow in the security/accessibility demos, iOS deployment target 13 → 15 (Xcode 26).
  • Web: the new tests (58) plus the full epub and clipboard suites pass on Chrome. No regex lookbehind anywhere in lib/.
  • Public-API check: a consumer project that depends on root + epub + clipboard (core/root overridden to this tree, since 1.11.0 is not on pub.dev yet) names every new public symbol through the public barrels and analyzes clean.
  • pana (overrides stripped): core 160/160, clipboard 160/160. Root and epub can only be scored once core 1.11.0 resolves on pub.dev.
  • Timing assertions: the performance bound is measured at ~1.1× (body/html rules 1.02×, override 1.09×, guard 1.12×) against a 3× limit, sampled as best-of-9; the hardening file passed 10/10 consecutive runs under heavy machine load.
  • CI on this PR: all checks green, including Golden (light-mode pixels unchanged).

Not verified

  • The 3-OS × 2-channel matrix (Windows, Linux, beta) only runs on push to main; there is no workflow_dispatch on test.yml, so it cannot be run before merging.
  • core's own suite on Chrome has 3 failing tests in performance_monitor_test.dart (Duration > 0 assertions with the coarse web timer). They fail identically on v1.10.0, are unrelated to this PR, and CI does not run core on Chrome.

Known limitation

Link blue, inline <code> / <pre> and <mark> colors are still tuned for light surfaces.

…oot only (#20)

HyperViewer's unstyled text was always the fixed dark gray #1F2937 with no
way to change it, so it was unreadable on dark surfaces.

- Dark Theme: default text color is colorScheme.onSurface. Light theme output
  is unchanged. A theme toggle re-resolves in place (no loading state, scroll
  position kept).
- New HyperViewer(textColor:), a host override that wins over the content's
  html/:root/body color but not over an element's own color.
- body { color } and html { color } now apply (no UDT node is tagged body or
  html, so they never matched). Layered html < :root < body.
- :root matched every top-level block because it tested parent == null, which
  top-level blocks also satisfy; it now tests node is DocumentNode.
- StyleResolver.ensureReadableOnOwnBackground: an element with its own opaque
  background and no color of its own no longer inherits text under 3:1
  contrast (blockquote, kbd, th, author backgrounds). Enabled only when the
  host supplies a default color.
- customCss doc example used body { font-size }, which is still ignored.
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 12 20
✅ mixed_inline 10 10 11
✅ float_layout 12 11 18
❌ table_20_rows 14 30 39
✅ cjk_ruby 14 7 8
❌ large_article 16 27 50

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

…r painted

A custom tag (<info-box>, <x-badge>) has no UA display style, so the HTML
adapters build it as an InlineNode. _handleInlineNode only consulted the
inline plugin tags, so a registered BLOCK plugin on such a tag never reached
_tokenizeBlockPlugin: its widget was built, linked to no fragment, sized 0x0
and not painted ('More child widgets than fragments' in debug).

A registered block tag is now handled as a block whatever its node type.

The existing plugin tests hand-built BlockNodes or asserted findsOneWidget,
which a 0x0 widget satisfies. The new tests assert size, position between the
neighbouring paragraphs, painted pixels, stacking, virtualized mode, that the
plugin owns its children, and that unregistered and inline plugins are
unaffected.
…emo fixes

- hyper_render_epub 0.1.3: EpubReader(textColor:) forwarded to HyperViewer;
  requires hyper_render/core ^1.11.0.
- Versions: hyper_render 1.11.0, hyper_render_core 1.11.0. CHANGELOGs, README
  (Dark Mode section, API table), MIGRATION_GUIDE, CSS matrix, ROADMAP.
- test/integration/dark_mode_hardening_test.dart: security (hostile body
  colors, var() bomb, 5000-rule sheet), stress (40 theme toggles in sync /
  virtualized / paged, in-flight parse, streaming) and performance.
- example: DEMO_THEME=dark|light define for the all-demos drive; fix a
  RenderFlex overflow in the accessibility demo (controls now scroll and are
  capped) and ListTile-inside-ColoredBox assertions in the security and
  accessibility demos (Material instead of Container(color:)).
- customCss dartdoc now says the document's own <style> wins at equal
  specificity.
…tyle

# Conflicts:
#	packages/hyper_render_core/CHANGELOG.md
… fixes, body/html !important layering

Docs (each was checked against the code):
- hyper_render_clipboard README, usage guide, example and dartdoc, and the root
  README, showed HyperViewer(imageClipboardHandler:) and onImageLongPress, which
  do not exist, so the documented setup did not compile. Rewritten to the real
  route (HyperImage via widgetBuilder); documented_usage_test.dart now compiles
  and runs that snippet.
- example/MULTIMEDIA_EXAMPLES.md used HyperViewer(mediaBuilder:) (a parameter of
  HtmlToSpanConverter only), a nonexistent HyperMediaBuilder, a missing example
  file and a stale issue URL. Rewritten to widgetBuilder; the unreleased
  hyper_render_media is labelled planned.
- Root README epub snippet used EpubBook.openBytes and EpubReader(book:,
  onChapterChanged:), neither of which exist.

Engine: a body color now sits above html / :root including their !important
forms (an element's own declaration beats what it inherits), as in a browser.
Tests cover html-important alone, html-important vs body, :root-important vs
body, and markdown in virtualized mode (the sync-fallback parse path).

Example app:
- ai_streaming_demo: the three stream buttons overflowed a 390pt phone by 21px
  (Row -> Wrap). Found by driving all 27 demos on the iOS simulator.
- example/ios: raise the deployment target 13 -> 15 (Xcode 26 rejects < 15), as
  macOS was raised to 12 in 1.10.0.

Verified: all 27 demos open with 0 FlutterErrors on iOS (dark theme); 100% of
the lines added in hyper_viewer.dart / resolver.dart are exercised by tests.
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 17 26
❌ mixed_inline 10 14 15
❌ float_layout 12 14 19
❌ table_20_rows 14 43 65
✅ cjk_ruby 14 9 11
❌ large_article 16 37 65

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

…e API, keep logs out of the archive

- hyper_render_clipboard 1.7.3: docs-only release so pub.dev stops showing the
  HyperViewer(imageClipboardHandler:) snippet that never compiled.
- Deprecate HyperRenderTheme / HyperRenderThemeData: nothing reads them, so
  setting them never had any effect.
- .pubignore: *.log and .DS_Store. .pubignore replaces .gitignore for pub, so
  the untracked flutter_0N.log crash reports (local paths, commands) were being
  packed into the 32 MB root archive.
- CHANGELOG: fix a bullet that contradicted itself about EpubReader.textColor
  and wrongly said EpubReader needs !important. HyperViewer.customCss precedes
  the document <style> (!important needed); EpubReader appends the reader's CSS
  after the chapter's (plain wins). Both directions now have a test.
- Known limitation noted: link / inline code / <mark> colors are still tuned
  for light surfaces.
- Rename a hardening test to say what it asserts.
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 15 23
❌ mixed_inline 10 13 13
✅ float_layout 12 12 21
❌ table_20_rows 14 32 53
✅ cjk_ruby 14 8 10
❌ large_article 16 26 47

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

…ce per round)

The 3x bound was measured at ~1.1x in isolation (body/html rules 1.02x,
override 1.09x, guard 1.12x) but read 2.07x once under build load with only 5
samples taken by an awkward double call. Take the minimum of 9 single runs per
variant so a busy runner does not flake it.
@vietnguyentuan2019 vietnguyentuan2019 changed the title fix: readable text on dark surfaces; body/html color; :root matches root only (#20) release: 1.11.0 — dark mode text color, body/html color, :root fix (#20), block plugins on custom tags (#22), docs fixes Oct 3, 2026
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ Layout Regression — Budget exceeded (informational)

Fixture Budget (ms) Median (ms) P95 (ms)
❌ simple_paragraph 8 17 25
❌ mixed_inline 10 14 15
❌ float_layout 12 14 29
❌ table_20_rows 14 50 77
✅ cjk_ruby 14 11 13
❌ large_article 16 40 82

One or more fixtures exceeded the 16 ms budget.

Flutter 3.41.5 · ubuntu-22.04

Informational, does not block merge: CI runners use software
rendering and are 2-3x slower than the devices the budgets are
calibrated for. Compare against the base branch; if a fixture
regressed relative to it, profile with:

flutter test benchmark/layout_regression.dart --reporter expanded

and check _performLineLayout / _buildCharacterMapping for
any new O(N²) or O(N log N) paths introduced in this PR.

@vietnguyentuan2019
vietnguyentuan2019 merged commit 7acdfb7 into main Oct 3, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Does this support dark mode?

1 participant